import { Matrix4 } from "../../utils/Matrix4";
import { WebGLUtil } from "../../utils/WebGLUtil";
import { Render } from "../Render";
import { WebglTemplateBase } from "../template/WebglTemplateBase";

/*
 * @Author: Snow
 * @Date: 2021-10-26 14:29:17
 * @Description: 实例化绘制 优化
 */
export class DrawCallInstance extends WebglTemplateBase {
    public static debug = true;
    protected override initProgram(): void {
        const gl = Render.GL;

        this.vsSource = `
            attribute vec4 a_position;
            attribute vec4 a_color;
            
            attribute mat4 a_matrix;
            
            varying vec4 v_color;
            
            void main() {
                // Multiply the position by the matrix.
                gl_Position = a_matrix * a_position;
                
                // Pass the color to the fragment shader.
                v_color = a_color;
            }
        `;

        this.fsSource = `
            precision mediump float;

            // Passed in from the vertex shader.
            varying vec4 v_color;
            
            void main() {
                gl_FragColor = v_color;
            }
        `;
        super.initProgram();
    }

    protected override initBuffers(): void {

        super.initBuffers();
        const gl = Render.GL;
        const ext = gl.getExtension('ANGLE_instanced_arrays');
        if (!ext) {
            return alert('need ANGLE_instanced_arrays');
        }

        const positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            -0.1, 0.4,
            -0.1, -0.4,
            0.1, -0.4,
            0.1, -0.4,
            -0.1, 0.4,
            0.1, 0.4,
            0.4, -0.1,
            -0.4, -0.1,
            -0.4, 0.1,
            -0.4, 0.1,
            0.4, -0.1,
            0.4, 0.1,
        ]), gl.STATIC_DRAW);

        const numVertices = 12;
        // 为每一个实例设置矩阵
        const numInstances = 5;
        // make a typed array with one view per matrix
        const matrixData = new Float32Array(numInstances * 16);

        const matrices = [];
        for (let i = 0; i < numInstances; ++i) {
            const byteOffsetToMatrix = i * 16 * 4;
            const numFloatsForView = 16;
            matrices.push(new Float32Array(
                matrixData.buffer,
                byteOffsetToMatrix,
                numFloatsForView));
        }

        const matrixBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);

        // 只为缓冲区申请特定大小的空间
        gl.bufferData(gl.ARRAY_BUFFER, matrixData.byteLength, gl.DYNAMIC_DRAW);

        // 为每一个实例设置颜色
        const colorBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER,
            new Float32Array([
                1, 0, 0, 1,  // red
                0, 1, 0, 1,  // green
                0, 0, 1, 1,  // blue
                1, 0, 1, 1,  // magenta
                0, 1, 1, 1,  // cyan
            ]),
            gl.STATIC_DRAW);


        let drawScene = () => {
            let time = this.ts;
            time = time * 0.5 + 5;
            WebGLUtil.resizeCanvasToMatchDisplaySize(gl);
            // Clear the canvas AND the depth buffer.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            let program = this.programInfo.program;

            gl.useProgram(program);

            const positionLoc = gl.getAttribLocation(program, 'a_position');
            const colorLoc = gl.getAttribLocation(program, 'a_color');
            const matrixLoc = gl.getAttribLocation(program, 'a_matrix');

            gl.bindBuffer(gl.ARRAY_BUFFER,positionBuffer);
            gl.enableVertexAttribArray(positionLoc);
            gl.vertexAttribPointer(positionLoc,2,gl.FLOAT,false,0,0);

            

            // 更新所有矩阵
            matrices.forEach((mat, ndx) => {
                let temp = Matrix4.translation(-0.5 + ndx * 0.25, 0, 0);
                temp = Matrix4.multiply(temp, Matrix4.rotateZ(time * (0.1 + 0.1 * ndx)));
                Matrix4.copy(temp, mat);
            });

            // 上传新的矩阵数据
            gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
            gl.bufferSubData(gl.ARRAY_BUFFER, 0, matrixData);


            const bytesPerMatrix = 4 * 16;
            for (let i = 0; i < 5; ++i) {
                const loc = matrixLoc + i;
                gl.enableVertexAttribArray(loc);
                // 注意stride和offset
                const offset = i * 16;  // 一行有4个单精度浮点数，1个就占用4字节
                gl.vertexAttribPointer(
                    loc,              // location
                    4,                // size (num values to pull from buffer per iteration)
                    gl.FLOAT,         // type of data in buffer
                    false,            // normalize
                    bytesPerMatrix,   // stride, num bytes to advance to get to next set of values
                    offset,           // offset in buffer
                );
                // 这行说的是attribute只对下一个实例才进行迭代
                ext.vertexAttribDivisorANGLE(loc, 1);
            }

            // 为颜色设置attribute
            gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
            gl.enableVertexAttribArray(colorLoc);
            gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
            // this line says this attribute only changes for each 1 instance
            ext.vertexAttribDivisorANGLE(colorLoc, 1);

            ext.drawArraysInstancedANGLE(
                gl.TRIANGLES,
                0,             // offset
                numVertices,   // 每个实例的顶点数
                numInstances,  // 实例的数量
              );

        }
        this.draw = drawScene;
    }
}